package tech.xs.common.framework.init.auth;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import tech.xs.common.framework.domain.annotations.AuthApi;
import tech.xs.common.framework.domain.constant.Symbol;
import tech.xs.common.framework.domain.constant.mq.QueueNames;
import tech.xs.common.framework.domain.enums.HttpMethodEnum;
import tech.xs.common.framework.init.InitExecute;
import tech.xs.common.framework.init.auth.domain.bo.sync.SyncAuthApiBo;
import tech.xs.common.framework.init.auth.domain.bo.sync.SyncAuthApiItemBo;
import tech.xs.common.framework.util.MqUtil;
import tech.xs.common.framework.util.SpringPackageUtil;

import java.lang.reflect.Method;
import java.util.*;

@Slf4j
@Component
@Order(400)
public class InitAuthApi implements InitExecute {

    @Value("${spring.application.name}")
    private String module;

    @Override
    public void execute() throws Exception {
        log.info("[MQ生产者][授权路径同步]开始");
        Set<Class<?>> controllerClasses = new HashSet<>();
        Collection<String> packageNames = SpringPackageUtil.getPackageNames();
        for (String packageName : packageNames) {
            controllerClasses.addAll(ClassUtil.scanPackageByAnnotation(packageName, RestController.class));
            controllerClasses.addAll(ClassUtil.scanPackageByAnnotation(packageName, Controller.class));
        }
        if (CollUtil.isEmpty(controllerClasses)) {
            log.info("[MQ生产者][授权路径同步]结束 获取Controller Class为空");
            MqUtil.send(QueueNames.AUTH_AUTH_PATH, new SyncAuthApiBo());
            return;
        }
        SyncAuthApiBo syncAuthApiBo = new SyncAuthApiBo();
        syncAuthApiBo.setModule(module);
        syncAuthApiBo.setApis(getAuthApiList(controllerClasses));
        log.info("[MQ生产者][授权路径同步] 发送内容:{}", JSONObject.toJSONString(syncAuthApiBo));
        MqUtil.send(QueueNames.AUTH_AUTH_PATH, syncAuthApiBo);
    }

    @Override
    public boolean isErrorStop() {
        return true;
    }

    private List<SyncAuthApiItemBo> getAuthApiList(Set<Class<?>> controllerClasses) {
        List<SyncAuthApiItemBo> authPathList = new ArrayList<>();
        for (Class<?> controllerClass : controllerClasses) {
            RequestMapping requestMapping = controllerClass.getAnnotation(RequestMapping.class);
            Method[] methods = controllerClass.getMethods();
            for (Method method : methods) {
                List<SyncAuthApiItemBo> methodAuthPathList = getAuthPathList(method);
                if (CollUtil.isEmpty(methodAuthPathList)) {
                    continue;
                }
                if (requestMapping == null) {
                    authPathList.addAll(methodAuthPathList);
                    continue;
                }
                authPathList.addAll(getControllerRequestMappingAuthApiList(requestMapping, methodAuthPathList));
            }
        }
        return authPathList;
    }

    private List<SyncAuthApiItemBo> getControllerRequestMappingAuthApiList(RequestMapping requestMapping, List<SyncAuthApiItemBo> methodAuthPathList) {
        String[] paths = requestMapping.path();
        if (ArrayUtil.isEmpty(paths)) {
            paths = requestMapping.value();
        }

        List<SyncAuthApiItemBo> authPathList = new ArrayList<>();
        for (String path : paths) {
            List<SyncAuthApiItemBo> methodAuthPathListClone = ObjectUtil.clone(methodAuthPathList);
            if (StrUtil.isBlank(path) || Symbol.SLASH_STR.equals(path)) {
                authPathList.addAll(methodAuthPathListClone);
                continue;
            }
            if (!path.startsWith(Symbol.SLASH_STR)) {
                path = Symbol.SLASH_STR + path;
            }
            for (SyncAuthApiItemBo authPath : methodAuthPathListClone) {
                authPath.setPath(path + authPath.getPath());
            }
            authPathList.addAll(methodAuthPathListClone);
        }
        return authPathList;
    }

    private List<SyncAuthApiItemBo> getAuthPathList(Method method) {
        List<SyncAuthApiItemBo> authPathList = new ArrayList<>();

        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
        if (requestMapping != null) {
            String[] paths = requestMapping.path();
            if (ArrayUtil.isEmpty(paths)) {
                paths = requestMapping.value();
            }
            HttpMethodEnum[] methods = HttpMethodEnum.values();
            for (HttpMethodEnum httpMethod : methods) {
                authPathList.addAll(getAuthPathList(method, paths, httpMethod));
            }
        }
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        if (getMapping != null) {
            authPathList.addAll(getAuthPathList(HttpMethodEnum.GET, getMapping.path(), getMapping.value(), method));
        }
        PostMapping postMapping = method.getAnnotation(PostMapping.class);
        if (postMapping != null) {
            authPathList.addAll(getAuthPathList(HttpMethodEnum.POST, postMapping.path(), postMapping.value(), method));
        }
        PutMapping putMapping = method.getAnnotation(PutMapping.class);
        if (putMapping != null) {
            authPathList.addAll(getAuthPathList(HttpMethodEnum.PUT, putMapping.path(), putMapping.value(), method));
        }
        DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
        if (deleteMapping != null) {
            authPathList.addAll(getAuthPathList(HttpMethodEnum.DELETE, deleteMapping.path(), deleteMapping.value(), method));
        }
        PatchMapping patchMapping = method.getAnnotation(PatchMapping.class);
        if (patchMapping != null) {
            authPathList.addAll(getAuthPathList(HttpMethodEnum.PATCH, patchMapping.path(), patchMapping.value(), method));
        }
        return authPathList;
    }

    private List<SyncAuthApiItemBo> getAuthPathList(HttpMethodEnum httpMethod, String[] paths, String[] value, Method method) {
        if (ArrayUtil.isEmpty(paths)) {
            paths = value;
        }
        return getAuthPathList(method, paths, httpMethod);
    }

    private List<SyncAuthApiItemBo> getAuthPathList(Method method, String[] paths, HttpMethodEnum httpMethod) {
        AuthApi authPathAnnotation = method.getAnnotation(AuthApi.class);
        if (authPathAnnotation == null) {
            return new ArrayList<>();
        }
        List<SyncAuthApiItemBo> authPathList = new ArrayList<>();
        for (String path : paths) {
            if (StrUtil.isBlank(path)) {
                continue;
            }
            if (!path.startsWith(Symbol.SLASH_STR)) {
                path = Symbol.SLASH_STR + path;
            }
            SyncAuthApiItemBo authPath = new SyncAuthApiItemBo();
            authPath.setType(authPathAnnotation.type());
            authPath.setMethod(httpMethod);
            authPath.setPath(path);
            authPath.setBasicAuth(authPathAnnotation.basicAuth());
            authPath.setPermissions(new ArrayList<>(Arrays.asList(authPathAnnotation.permissions())));
            authPath.setRoles(new ArrayList<>(Arrays.asList(authPathAnnotation.roles())));
            authPathList.add(authPath);
        }
        return authPathList;
    }


}
